#include "multi_button.h"

#define EVENT_CB(ev)   if(handle->cb[ev]) handle->cb[ev]((Button*)handle)
	
//全局静态变量 就在自己的模块活动 一开始是Null 随后头插的时候实例化做成链表
static struct Button* head_handle = NULL;

//初始化一个按键节点 都是缺损值
void button_init(struct Button* handle, uint8_t(*pin_level)(), uint8_t active_level)
{
	memset(handle, 0, sizeof(struct Button));
	handle->event = (uint8_t)NONE_PRESS;
	handle->hal_button_Level = pin_level;
	handle->button_level = handle->hal_button_Level();
	handle->active_level = active_level;
}

//按键添加回调函数
void button_attach(struct Button* handle, PressEvent event, BtnCallback cb)
{
	handle->cb[event] = cb;
}

//对应上面 一个set 一个get
PressEvent get_button_event(struct Button* handle)
{
	return (PressEvent)(handle->event);
}

//按键节点的处理函数 完整的状态机之王
void button_handler(struct Button* handle)
{
	uint8_t read_gpio_level = handle->hal_button_Level();

	//ticks counter working..
	if((handle->state) > 0) handle->ticks++;

	/*------------button debounce handle消抖---------------*/
	if(read_gpio_level != handle->button_level) 
 { //not equal to prev one
		//continue read 3 times same new level change
		if(++(handle->debounce_cnt) >= DEBOUNCE_TICKS)
			{
				handle->button_level = read_gpio_level;
				handle->debounce_cnt = 0;
			}
	} 
 else 
  { //leved not change ,counter reset.
		handle->debounce_cnt = 0;
	}

	/*-----------------State machine-------------------*/
	switch (handle->state) {
	case 0:
		if(handle->button_level == handle->active_level) {	//start press down
			handle->event = (uint8_t)PRESS_DOWN;
			EVENT_CB(PRESS_DOWN);
			handle->ticks = 0;
			handle->repeat = 1;
			handle->state = 1;
		} else {
			handle->event = (uint8_t)NONE_PRESS;
		}
		break;

	case 1:
		if(handle->button_level != handle->active_level) { //released press up
			handle->event = (uint8_t)PRESS_UP;
			EVENT_CB(PRESS_UP);
			handle->ticks = 0;
			handle->state = 2;

		} else if(handle->ticks > LONG_TICKS) {
			handle->event = (uint8_t)LONG_RRESS_START;
			EVENT_CB(LONG_RRESS_START);
			handle->state = 5;
		}
		break;

	case 2:
		if(handle->button_level == handle->active_level) { //press down again
			handle->event = (uint8_t)PRESS_DOWN;
			EVENT_CB(PRESS_DOWN);
			handle->repeat++;
			if(handle->repeat == 2) {
				EVENT_CB(DOUBLE_CLICK); // repeat hit
			} 
			EVENT_CB(PRESS_REPEAT); // repeat hit
			handle->ticks = 0;
			handle->state = 3;
		} else if(handle->ticks > SHORT_TICKS) { //released timeout
			if(handle->repeat == 1) {
				handle->event = (uint8_t)SINGLE_CLICK;
				EVENT_CB(SINGLE_CLICK);
			} else if(handle->repeat == 2) {
				handle->event = (uint8_t)DOUBLE_CLICK;
			}
			handle->state = 0;
		}
		break;

	case 3:
		if(handle->button_level != handle->active_level) { //released press up
			handle->event = (uint8_t)PRESS_UP;
			EVENT_CB(PRESS_UP);
			if(handle->ticks < SHORT_TICKS) {
				handle->ticks = 0;
				handle->state = 2; //repeat press
			} else {
				handle->state = 0;
			}
		}
		break;

	case 5:
		if(handle->button_level == handle->active_level) {
			//continue hold trigger
			handle->event = (uint8_t)LONG_PRESS_HOLD;
			EVENT_CB(LONG_PRESS_HOLD);

		} else { //releasd
			handle->event = (uint8_t)PRESS_UP;
			EVENT_CB(PRESS_UP);
			handle->state = 0; //reset
		}
		break;
	}
}

//增加一个节点到链表
//最开始没有节点过来就是NULL [因为head_handle静态全局变量是NULL]
//第一次：A过来  A->NULL
//第二次: B过来  B->A->NULL
//体会一下头插就是2句话  新过来的指向头部 再新过来当头即可
int button_start(struct Button* handle)
{
	/*****下面这部分代码可以不要 它是遍历查找 不允许重复的***/
	struct Button* target = head_handle;
	while(target) 
	{
		if(target == handle) return -1;	//already exist.
		target = target->next;
	}
   /*****上面这部分代码可以不要 它是遍历查找 不允许重复的***/
	handle->next = head_handle;
	head_handle = handle;
	return 0;
}

//删除一个节点
//大道至简 看我的list.c来理解这个神话吧
void button_stop(struct Button* handle)
{
	struct Button** curr;
	for(curr = &head_handle; *curr; ) 
	{
		struct Button* entry = *curr;
		if (entry == handle)
			*curr = entry->next;
		else
			curr = &entry->next;
	}
}

//测试函数 遍历整个链表 如果节点为实 则进去处理
//放在时驱函数中 TIME或者任务中期性调用
void button_ticks()
{
	for(struct Button* target=head_handle; target; target=target->next)	button_handler(target);
}

